﻿using System;
using AgencyManagerSystem.Common;
using Dapper.Contrib.Extensions;
using Dapper;
using System.Data;
using System.Reflection;
using AgencyManagerSystem.DataFetcher;
using AgencyManagerSystem.Model;
using System.Collections.Generic;
using System.Linq;

namespace AgencyManagerSystem.DataUpdater
{
    public abstract class MainDetailUpdater<TMain,TDetail> where TMain : DBModel where TDetail:DBModel
    {
        protected BaseUpdater<TMain> baseUpdater;
        protected BaseFetcher<TMain> fetcher;
        protected DetailFetcher<TDetail> detailFetcher;
        protected string detailTableName;
        internal MainDetailUpdater(BaseFetcher<TMain> mainFetcher, DetailFetcher<TDetail> detailFetcher, BaseUpdater<TMain> updater) 
        {
            TableAttribute tableNameAttribute = typeof(TDetail).GetCustomAttribute<TableAttribute>();
            if (tableNameAttribute != null)
            {
                detailTableName = tableNameAttribute.Name;
            }
            this.fetcher = mainFetcher;
            this.detailFetcher = detailFetcher;
            this.baseUpdater = updater;
        }
        //protected abstract void SetMainId(IList<TDetail> detail,int mainId);
        //protected abstract string GetMainIdField();
        public virtual event DataBulkUpdatingHandler<TMain,TDetail> DataBulkUpdating;
        public virtual event DataBulkDeletingHandler<TMain, TDetail> DataBulkDeleting;
        private PropertyInfo GetMainProperty()
        {
            PropertyInfo[] propertyInfos = typeof(TDetail).GetProperties();
            PropertyInfo mainProperty = null;
            foreach (PropertyInfo property in propertyInfos)
            {
                if (property.GetCustomAttribute<MainIdAttribute>() != null)
                {
                    mainProperty = property;
                    break;
                }
            }
            return mainProperty;
        }
        public virtual int BulkUpdate(TMain data, IEnumerable<TDetail> detail, out string message)
        {
            if (data == null || detail == null || detailFetcher == null)
            {
                LogHelper.Err(this.GetType().Name, "BULKUPDATE:传入的参数为空");
                message = "内部错误,传入的参数异常";
                return 0;
            }
            IDbTransaction dbTransaction = DBHelper.GetTran();
            IList<TDetail> dbDetails = detailFetcher.ListDetail(data.AutoId);
            DataBulkUpdatingEventArgs<TMain, TDetail> dataUpdatingEventArgs = new DataBulkUpdatingEventArgs<TMain, TDetail>()
            {
                Insert = data,
                DetailDelete = dbDetails,
                DetailInsert = detail
            };
            if (DataBulkUpdating != null)
            {
                LogHelper.Log(this.GetType().Name, "BULKUPDATE :正在检查从表数据合法性");
                foreach (DataBulkUpdatingHandler<TMain, TDetail> handler in DataBulkUpdating.GetInvocationList())
                {
                    handler(this, dataUpdatingEventArgs);
                    if (dataUpdatingEventArgs.Cancel)
                    {
                        message = dataUpdatingEventArgs.Message;
                        LogHelper.Warn(this.GetType().Name, "BULKUPDATE:检查从表数据合法性不通过：" + message);
                        DBHelper.Commit(false);
                        return 0;
                    }
                }
            }
            int dataId = baseUpdater.Save(data, out message);
            if (dataId == 0)
            {
                LogHelper.Err(this.GetType().Name, "BULKUPDATE:主表数据写入失败");
                return dataId;
            }
            PropertyInfo mainProperty = GetMainProperty();
            foreach (TDetail item in detail)
            {
                if (mainProperty != null)
                    mainProperty.SetValue(item,dataId);
            }
            LogHelper.Log(this.GetType().Name, String.Format("BULKUPDATE:从数据库中创建/更新{0}对象", detailTableName));
            long insCnt, updCnt, delCnt;
            insCnt = updCnt = delCnt = 0;
            IEnumerable<TDetail>[] split = baseUpdater.ListSplit(detail, dbDetails);
            LogHelper.Log(this.GetType().Name, String.Format("BULKUPDATE:共有{1}个{0}对象将被写入,{2}个被更新，{3}个被删除", detailTableName,split[0].Count(),split[1].Count(),split[2].Count()));
            int toInsCnt = split[0].Count();
            int toUpdCnt = split[1].Count();
            int toDelCnt = split[2].Count();
            foreach (TDetail ins in split[0])
            {
                ins.Flag += 1;
                long r = dbTransaction.Insert(ins);
                if (r != 0) insCnt += 1;else ins.Flag -= 1;
                ins.AutoId = (int)r;
            }
            foreach (TDetail upd in split[1])
            {
                upd.Flag += 1;
                bool b = dbTransaction.Update(upd);
                if (b)updCnt += 1; else upd.Flag -= 1;
            }
            foreach (TDetail dlt in split[2])
            {
                bool b = dbTransaction.Delete(dlt);
                if (b) delCnt += 1;
            }
            if (insCnt != toInsCnt || updCnt != toUpdCnt || delCnt != toDelCnt)
            {
                LogHelper.Err(this.GetType().Name, String.Format("BULKUPDATE:从数据库中写入{0}对象失败", baseUpdater.TableName));
                message = "档案写入不成功";
                DBHelper.Commit(false);
                return 0;
            }
            return (int)dataId;
        }
        public virtual bool BulkDeleting(int autoid,  out string message)
        {
            message = "删除成功";
            if (detailFetcher == null)
            {
                LogHelper.Err(this.GetType().Name, "BULKDELETING:传入的参数异常");
                message = "内部错误,传入的参数异常";
                return false;
            }
            LogHelper.Log(this.GetType().Name, String.Format("BULKDELETING:从数据库中删除{0}对象", detailTableName));
            IDbTransaction dbTransaction = DBHelper.GetTran();
            IList<TDetail> dbDetails = detailFetcher.ListDetail(autoid);
            TMain data = fetcher.Get(autoid);
            if (data == null)
            {
                return true;
            }
            DataBulkDeletingEventArgs<TMain, TDetail> dataDeletingEventArgs = new DataBulkDeletingEventArgs<TMain, TDetail>()
            {
                Delete = data,
                DetailDelete = dbDetails,
            };
            if (DataBulkDeleting != null)
            {
                LogHelper.Log(this.GetType().Name, "BULKDELETING :正在检查数据合法性");
                foreach (DataBulkDeletingHandler<TMain, TDetail> handler in DataBulkDeleting.GetInvocationList())
                {
                    handler(this, dataDeletingEventArgs);
                    if ((dataDeletingEventArgs.Cancel))
                    {
                        message = dataDeletingEventArgs.Message;
                        LogHelper.Warn(this.GetType().Name, "BULKDELETING:检查数据合法性不通过：" + message);
                        DBHelper.Commit(false);
                        return false;
                    }
                }
            }
            bool delDetailSuccess = false;
            try
            {
                PropertyInfo mainProperty = GetMainProperty();
                delDetailSuccess = dbTransaction.Execute(String.Format("DELETE FROM {0} WHERE {1} = {2}", detailTableName, mainProperty.Name, data.AutoId))==dbDetails.Count;
                if (!delDetailSuccess)
                {
                    message = "删除出错";
                }
            }
            catch (Exception ex)
            {
                delDetailSuccess = false;
                message = ex.Message;
            }
            if (!delDetailSuccess)
            {
                LogHelper.Warn(this.GetType().Name, "BULKDELETING:删除明细行失败：" + message);
                DBHelper.Commit(false);
                message = "删除明细行失败";
                return false;
            }
            bool success = baseUpdater.Delete(data, out message);
            return success;
        }
        public virtual bool BulkApprove(int autoid,ApproveCode approveCode,out string message)
        {
            message = "审核成功";
            if (detailFetcher == null)
            {
                LogHelper.Err(this.GetType().Name, "BULKAPPROVE:传入的参数异常");
                message = "内部错误,传入的参数异常";
                return false;
            }
            LogHelper.Log(this.GetType().Name, String.Format("BULKAPPROVE:从数据库中审核{0}对象", detailTableName));
            IDbTransaction dbTransaction = DBHelper.GetTran();
            IList<TDetail> dbDetails = detailFetcher.ListDetail(autoid);
            TMain data = fetcher.Get(autoid);
            if (data == null)
            {
                return false;
            }
            return baseUpdater.Approve(autoid,approveCode,out message);
        }
    }
}
